package manager;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.StringTokenizer;

import programme.PAQ;
import programme.Programme;
import resolution.CPlex;
import resolution.VNS;
import GUI.PNEGUI;

/**
 * Cette classe est un manager pour l'application développée ici.
 * Elle gère l'interface utilisateur, les données, le programme.
 * C'est par elle que l'on lancera les calculs VNS et CPlex.
 * 
 * @author BERNARD Arthur, DELAMARCHE Jérôme, PAVAGEAU Max
 */
public class Manager {
	/**
	 * Represente la solution au probleme. Peut-etre la borne duale ou primale.
	 */
	private Solution solution;
	/**
	 * Reference vers l'interface graphique du programme.
	 */
	private PNEGUI interfaceGraphique;
	/**
	 * Reference vers le programme qui sera traite.
	 */
	private Programme programme;
	/**
	 * Les paramètres de l'application et du programme mathématique.
	 */
	private ParameterSet param = new ParameterSet();
	
	/**
	 * Le fichier dans lequel seront écrits les logs.
	 */
	private PrintWriter log = null;
	
	public Solution getSolution() {
		return solution;
	}
	public void setSolution(Solution solution) {
		this.solution = solution;
	}
	public PNEGUI getInterfaceGraphique() {
		return interfaceGraphique;
	}
	public void setInterfaceGraphique(PNEGUI interfaceGraphique) {
		this.interfaceGraphique = interfaceGraphique;
	}
	public Programme getProgramme() {
		return programme;
	}
	public void setProgramme(Programme programme) {
		this.programme = programme;
	}
	public ParameterSet getParam() {
		return param;
	}
	
	/**
	 * Méthode qui parse un fichier pour en tirer un ensemble de paramètres pour la résolution d'un programme PAQ.
	 * 
	 * @param file Le fichier à parser.
	 * @return Un ensemble de paramètres tirés du fichier.
	 */
	public void readFromFile(File file) {
		try {
			BufferedReader buf = new BufferedReader(new FileReader(file));
			Donnees data = null;
			//param = new ParameterSet();
			StringTokenizer token;
			String curLine, word, varSelected = null;
			int state = 3, lineN = 1, count = 0;
			
			
			curLine = buf.readLine();
			while(curLine != null) {
				
				token = new StringTokenizer(curLine);
				
				while(token.hasMoreTokens()) {
					word = token.nextToken();
					
					if(word.equals("="))
						continue;
					else if(word.equalsIgnoreCase("[options]")) {
						state = 1;
						varSelected = null;
						continue;
					}
					else if(word.equalsIgnoreCase("[vnsparams]")) {
						state = 2;
						varSelected = null;
						continue;
					}
					else if(word.equalsIgnoreCase("[data]")) {
						state = 3;
						varSelected = null;
						continue;
					}
					
					switch(state) {
						case 1:	// options de l'application
							if(varSelected == null) {
								if(word.equalsIgnoreCase("cplex"))
									varSelected = "cplex";
								else if(word.equalsIgnoreCase("vns"))
									varSelected = "vns";
								else if(word.equalsIgnoreCase("log"))
									varSelected = "log";
								else if(word.equalsIgnoreCase("verbose"))
									varSelected = "verbose";
								else
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
							}
							else {
								if(word.equalsIgnoreCase("false")) {
									param.setParam(varSelected, false);
									varSelected = null;
								}
								else if(word.equalsIgnoreCase("true")) {
									param.setParam(varSelected, true);
									varSelected = null;
								}
								else
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
							}
						break;
						case 2:	// paramètres VNS
							if(varSelected == null) {
								if(word.equalsIgnoreCase("nbitermax"))
									varSelected = "nbitermax";
								else if(word.equalsIgnoreCase("nbvisitmax"))
									varSelected = "nbvisitmax";
								else if(word.equalsIgnoreCase("epsilon"))
									varSelected = "epsilon";
								else
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
							}
							else {
								try {
									param.setParam(varSelected, Double.parseDouble(word));
								}
								catch(NumberFormatException E) {
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
								}
								
								varSelected = null;
							}
						break;
						case 3:	// données du PAQ
							if(varSelected == null) {
								data = new Donnees(Integer.parseInt(word));
								varSelected = "length";
							}
							else if(varSelected.equals("length")){
								try {
									data.tabDist[count/data.taille][count%data.taille] = Double.parseDouble(word);
								}
								catch(NumberFormatException E) {
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
								}
								count++;
								
								if(count == data.taille * data.taille) {
									count = 0;
									varSelected = "tabdist";
								}
							}
							else if(varSelected.equals("tabdist")){
								try {
									data.tabFlux[count/data.taille][count%data.taille] = Double.parseDouble(word);
								}
								catch(NumberFormatException E) {
									throw(new WrongPNEFileFormatException(file.getName(), lineN));
								}
								count++;
								
								if(count == data.taille * data.taille) {
									count = 0;
									varSelected = null;
									state = 0;
								}
							}
						break;
						default:
							throw(new WrongPNEFileFormatException(file.getName(), lineN));
					}
				}
				
				curLine = buf.readLine();
				lineN++;
			}
			
			programme = new PAQ(data);
			interfaceGraphique.notifyParamChanged(param);
		}
		catch(IOException E) {
			E.printStackTrace();
			param = new ParameterSet();
		}
		catch(WrongPNEFileFormatException E) {
			E.printStackTrace();
			param = new ParameterSet();
		}
	}
	
	public void printOut(String string) {
		if(param.getBool("verbose"))
			interfaceGraphique.printOutln(string);
		
		if(param.getBool("log"))
			log.println(string);
	}
	
	/**
	 * Méthode permettant de lancer le processus de calcul en fonction du ParameterSet.
	 */
	public void performProcess() {
		Solution solution;
		interfaceGraphique.disableActions();
		log = null;
		Date dateLog = new Date();
		
		if(programme != null) {			
			if(param.getBool("cplex")) {
				if(param.getBool("log")) {
					try {
						log = new PrintWriter(new BufferedWriter(new FileWriter("CPlexLog_" + dateLog.getHours() + "_" + dateLog.getMinutes() + "_" + dateLog.getSeconds() + ".txt")));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

				PrintStream tmp = System.out;
				RedirectionSortie sortie = new RedirectionSortie(interfaceGraphique, log);
				
				Date depart = new Date();
				solution = CPlex.rechercherBorneDuale(programme);
				Date fin = new Date();
				long lol = fin.getTime() - depart.getTime();

				interfaceGraphique.printOutln("");
				interfaceGraphique.printOutln("Solution obtenue en " + lol/60000 + "min " + (lol/1000)%60 + "s " + lol%1000 + "ms.");
				interfaceGraphique.printOutln("");

				if(log != null) {
					log.println("");
					log.println("Solution obtenue en " + lol/60000 + "min " + (lol/1000)%60 + "s " + lol%1000 + "ms.");
					log.println("");
				}
				
				System.setOut(tmp);
				
				if(log != null)
					log.close();
			}
			
			log = null;
			
			if(param.getBool("vns")) {
				if(param.getBool("log")) {
					try {
						log = new PrintWriter(new BufferedWriter(new FileWriter("VNSLog_" + dateLog.getHours() + "_" + dateLog.getMinutes() + "_" + dateLog.getSeconds() + ".txt")));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				
				//Paramétrage de VNS
				VNS.eps = Double.parseDouble(interfaceGraphique.getEpsilonText().getText());
				VNS.nbIterMax = Integer.parseInt(interfaceGraphique.getNbIterMaxText().getText());
				VNS.nbVisitMax = Integer.parseInt(interfaceGraphique.getNbVisitMaxText().getText());
				VNS.programme = programme;
				VNS.manager = this;
				
				Date depart = new Date();
				solution = VNS.rechercherBornePrimale(param.getBool("verbose"), (log != null));
				Date fin = new Date();
				long lol = fin.getTime() - depart.getTime();
				
				interfaceGraphique.printOutln("--Solution finale--");
				interfaceGraphique.printOutln("");
				interfaceGraphique.printOutln("Solution : " + solution + "/Coût : " + programme.fonctionObjectif(solution));
				interfaceGraphique.printOutln("");
				interfaceGraphique.printOutln("Solution obtenue en " + lol/60000 + "min " + (lol/1000)%60 + "s " + lol%1000 + "ms.");
				interfaceGraphique.printOutln("");
				
				if(log != null) {
					log.println("--Solution finale--");
					log.println("");
					log.println("Solution : " + solution + "/Coût : " + programme.fonctionObjectif(solution));
					log.println("");
					log.println("Solution obtenue en " + lol/60000 + "min " + (lol/1000)%60 + "s " + lol%1000 + "ms.");
					log.println("");
				}
				
				if(log != null)
					log.close();
			}
		}
		
		interfaceGraphique.enableActions();
	}

	
	/****** MAIN ******/
	public static void main(String[] args) {
		Manager mgr = new Manager();
		mgr.setInterfaceGraphique(new PNEGUI(mgr));
		
		/*Donnees data = new Donnees(12);
		//Chr12b
		data.tabDist = new double[][] {
				{0,90,10,23,43,88,0,0,0,0,0,0},
				{90,0,0,0,0,0,0,0,0,0,0,0},
				{10,0,0,0,0,0,26,16,0,0,0,0},
				{23,0,0,0,0,0,0,0,0,0,0,0},
				{43,0,0,0,0,0,0,0,1,96,29,37},
				{88,0,0,0,0,0,0,0,0,0,0,0},
				{0,0,26,0,0,0,0,0,0,0,0,0},
				{0,0,16,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,1,0,0,0,0,0,0,0},
				{0,0,0,0,96,0,0,0,0,0,0,0},
				{0,0,0,0,29,0,0,0,0,0,0,0},
				{0,0,0,0,37,0,0,0,0,0,0,0} };
		data.tabFlux = new double[][] {
				{0,36,54,26,59,72,9,34,79,17,46,95},
				{36,0,73,35,90,58,30,78,35,44,79,36},
				{54,73,0,21,10,97,58,66,69,61,54,63},
				{26,35,21,0,93,12,46,40,37,48,68,85},
				{59,90,10,93,0,64,5,29,76,16,5,76},
				{72,58,97,12,64,0,96,55,38,54,0,34},
				{9,30,58,46,5,96,0,83,35,11,56,37},
				{34,78,66,40,29,55,83,0,44,12,15,80},
				{79,35,69,37,76,38,35,44,0,64,39,33},
				{17,44,61,48,16,54,11,12,64,0,70,86},
				{46,79,54,68,5,0,56,15,39,70,0,18},
				{95,36,63,85,76,34,37,80,33,86,18,0} };
				
		// Chr12a
		/*data.tabDist = new double[][] {
				{0,90,10,23,43,0,0,0,0,0,0,0},
				{90,0,0,0,0,88,0,0,0,0,0,0},
				{10,0,0,0,0,0,26,16,0,0,0,0},
				{23,0,0,0,0,0,0,0,0,0,0,0},
				{43,0,0,0,0,0,0,0,0,0,0,0},
				{0,88,0,0,0,0,0,0,1,0,0,0},
				{0,0,26,0,0,0,0,0,0,0,0,0},
				{0,0,16,0,0,0,0,0,0,96,0,0},
				{0,0,0,0,0,1,0,0,0,0,29,0},
				{0,0,0,0,0,0,0,96,0,0,0,37},
				{0,0,0,0,0,0,0,0,29,0,0,0},
				{0,0,0,0,0,0,0,0,0,37,0,0} };
		data.tabFlux = new double[][] {
				{0,36,54,26,59,72,9,34,79,17,46,95},
				{36,0,73,35,90,58,30,78,35,44,79,36},
				{54,73,0,21,10,97,58,66,69,61,54,63},
				{26,35,21,0,93,12,46,40,37,48,68,85},
				{59,90,10,93,0,64,5,29,76,16,5,76},
				{72,58,97,12,64,0,96,55,38,54,0,34},
				{9,30,58,46,5,96,0,83,35,11,56,37},
				{34,78,66,40,29,55,83,0,44,12,15,80},
				{79,35,69,37,76,38,35,44,0,64,39,33},
				{17,44,61,48,16,54,11,12,64,0,70,86},
				{46,79,54,68,5,0,56,15,39,70,0,18},
				{95,36,63,85,76,34,37,80,33,86,18,0} };*/
		
		//Chr12c
		/*data.tabDist = new double[][] {
				{0, 90, 10,  0,  0,  0,  0,  0,  0,  0, 0,  0},
				{90,  0,  0, 23,  0,  0,  0,  0,  0,  0, 0,  0},
				{10,  0,  0,  0, 43,  0,  0,  0,  0,  0, 0,  0},
				{0, 23,  0,  0,  0, 88,  0,  0,  0,  0, 0,  0},
				{0,  0, 43,  0,  0,  0, 26,  0,  0,  0, 0,  0},
				{0,  0,  0, 88,  0,  0,  0, 16,  0,  0, 0,  0},
				{0,  0,  0,  0, 26,  0,  0,  0,  1,  0, 0,  0},
				{0,  0,  0,  0,  0, 16,  0,  0,  0, 96, 0,  0},
				{0,  0,  0,  0,  0,  0,  1,  0,  0,  0, 29,  0},
				{0,  0,  0,  0,  0,  0,  0, 96,  0,  0, 0, 37},
				{0,  0,  0,  0,  0,  0,  0,  0, 29,  0, 0,  0},
				{0,  0,  0,  0,  0,  0,  0,  0,  0, 37, 0,  0} };
		data.tabFlux = new double[][] {
				{0, 36, 54, 26, 59, 72,  9, 34, 79, 17, 46, 95},
				{36,  0, 73, 35, 90, 58, 30, 78, 35, 44, 79, 36},
				{54, 73,  0, 21, 10, 97, 58, 66, 69, 61, 54, 63},
				{26, 35, 21,  0, 93, 12, 46, 40, 37, 48, 68, 85},
				{59, 90, 10, 93,  0, 64,  5, 29, 76, 16, 5, 76},
				{72, 58, 97, 12, 64,  0, 96, 55, 38, 54, 0, 34},
				{9, 30, 58, 46,  5, 96,  0, 83, 35, 11, 56, 37},
				{34, 78, 66, 40, 29, 55, 83,  0, 44, 12, 15, 80},
				{79, 35, 69, 37, 76, 38, 35, 44,  0, 64, 39, 33},
				{17, 44, 61, 48, 16, 54, 11, 12, 64,  0, 70, 86},
				{46, 79, 54, 68,  5,  0, 56, 15, 39, 70, 0, 18},
				{95, 36, 63, 85, 76, 34, 37, 80, 33, 86, 18,  0} };*/
		
		/*mgr.programme = new PAQ(data);
		Solution sol = new Solution(data.taille);
		//Chr12b
		sol.setSolution(new int[]{4, 6, 0, 9, 10, 2, 3, 1, 8, 5, 11, 7});
		// Chr12a
		//sol.setSolution(new int[]{6, 4, 11, 1, 0, 2, 8, 10, 9, 5, 7, 3});
		// Chr12c
		//sol.setSolution(new int[] {6,4,0,2,9,3,7,5,8,10,1,11});
		System.out.println(mgr.programme.fonctionObjectif(sol));
		Date depart = new Date();
		sol = CPlex.rechercherBorneDuale(mgr.programme);
		Date fin = new Date();
		//System.out.println(sol + " " + mgr.programme.fonctionObjectif(sol));
		long lol = fin.getTime() - depart.getTime();
		System.out.println("Solution obtenue en " + lol/60000 + "min " + (lol/1000)%60 + "s " + lol%1000 + "ms.");
		// Un moment il a réussi à trouver la solution obtimale en moins de 3 secondes, trop ouf (trop de la moule surtout).*/
	}
}
